package awk.polymorphic.service;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GenericTest {

	//这个类是个泛型类，在上面已经介绍过
	public static class Generic<T> {
		private T key;

		//虽然在方法中使用了泛型，但是这并不是一个泛型方法。这只是类中一个普通的成员方法，只不过他的返回值是在声明泛型类已经声明过的泛型。
		//所以在这个方法中才可以继续使用 T 这个泛型。
		public T getKey() {
			return key;
		}

		public void show_1(T t){
			System.out.println(t.toString());
		}

		//在泛型类中声明了一个泛型方法，使用泛型E，这种泛型E可以为任意类型。可以类型与T相同，也可以不同。
		//由于泛型方法在声明的时候会声明泛型<E>，因此即使在泛型类中并未声明泛型，编译器也能够正确识别泛型方法中识别的泛型。
		public <E> void show_3(E t){
			System.out.println(t.toString());
		}

		//在泛型类中声明了一个泛型方法，使用泛型T，注意这个T是一种全新的类型，可以与泛型类中声明的T不是同一种类型。
		public <T> void show_2(T t){
			System.out.println(t.toString());
		}

		//泛型方法与可变参数 printMsg("111",222,"aaaa","2323.4",55.55);
		public <T> void printMsg( T... args){
			for(T t : args){
				log.info("泛型测试","t is " + t);
			}
		}
		/**
		 * 这个方法是有问题的，编译器会为我们提示错误信息："UnKnown class 'E' "
		 * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
		 * 但是只声明了泛型类型T，并未声明泛型类型E，因此编译器并不知道该如何处理E这个类型。
		 public <T> T showKeyName(Generic<E> container){
		 ...
		 }
		 */

		/**
		 * 这个方法也是有问题的，编译器会为我们提示错误信息："UnKnown class 'T' " 对于编译器来说T这个类型并未项目中声明过，
		 * 因此编译也不知道该如何编译这个类。 所以这也不是一个正确的泛型方法声明。
		 *
		 * public void showkey(T genericObj){
		 * <p>
		 * }
		 */

	}

	/**
	 * 这才是一个真正的泛型方法。 首先在public与返回值之间的<T>必不可少，这表明这是一个泛型方法，
	 * 并且声明了一个泛型T 这个T可以出现在这个泛型方法的任意位置. 泛型的数量也可以为任意多个 如：
	 * public <T,K> K showKeyName(Generic<T>
	 * container){ ... }
	 *
	 * 这个泛型方法使用使用泛型T，注意这个T是一种全新的类型，可以与泛型类中声明的T不是同一种类型。
	 * 注意这个方法是在 GenericTest 类中，该类不是泛型类
	 */
	public <T> T showKeyName(Generic<T> container) {
		System.out.println("container key :" + container.getKey());
		//当然这个例子举的不太合适，只是为了说明泛型方法的特性。
		T test = container.getKey();
		return test;
	}


	//这也不是一个泛型方法，这就是一个普通的方法，只是使用了Generic<Number>这个泛型类做形参而已。
	public void showKeyValue1(Generic<Number> obj) {
		log.info("泛型测试", "key value is " + obj.getKey());
	}

	//这也不是一个泛型方法，这也是一个普通的方法，只不过使用了泛型通配符?
	//同时这也印证了泛型通配符章节所描述的，?是一种类型实参，可以看做为Number等所有类的父类
	public void showKeyValue2(Generic<?> obj) {
		log.info("泛型测试", "key value is " + obj.getKey());
	}

	//在泛型方法中添加上下边界限制的时候，必须在权限声明与返回值之间的<T>上添加上下边界，即在泛型声明的时候添加
//public <T> T showKeyName(Generic<T extends Number> container)，编译器会报错："Unexpected bound"
	public <T extends Number> T showKeyName3(Generic<T> container){
		System.out.println("container key :" + container.getKey());
		T test = container.getKey();
		return test;
	}


	public static class Fruit{
		@Override
		public String toString() {
			return "fruit";
		}
	}

	public static class Apple extends Fruit{
		@Override
		public String toString() {
			return "apple";
		}
	}

	public static class Person{
		@Override
		public String toString() {
			return "Person";
		}
	}

	public static void main(String[] args) {
		Apple apple = new Apple();
		Person person = new Person();

		Generic<Fruit> generateTest = new Generic<>();
		//apple是Fruit的子类，所以这里可以
		generateTest.show_1(apple);
		//编译器会报错，因为泛型类型实参指定的是Fruit，而传入的实参类是Person
		//generateTest.show_1(person);

		//使用这两个方法都可以成功
		generateTest.show_2(apple);
		generateTest.show_2(person);

		//使用这两个方法也都可以成功
		generateTest.show_3(apple);
		generateTest.show_3(person);
	}


}
